/*
  Copyright (C) 2011, Kyungjoo Kim
  
  This file is part of UHM (Unassembled HyperMatrix) sparse direct solver.
  
  All rights reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.
  
  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.
  
  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  Also add information on how to contact you by electronic and paper mail.
  
  
  Kyungjoo Kim
  iamkyungjoo@gmail.com
*/
#include "uhm/common.hxx"
#include "uhm/const.hxx"
#include "uhm/util.hxx"

#include "uhm/object.hxx"

#include "uhm/operation/scheduler.hxx"
#include "uhm/operation/mesh.hxx"
#include "uhm/operation/element.hxx"

#include "uhm/mesh/node.hxx"
#include "uhm/mesh/element.hxx"

#include "uhm/matrix/uhm/matrix.hxx"

#include "uhm/mesh/mesh.hxx"

//#define UHM_BUILD_GRAPH_MEASURE_TIME 1

namespace uhm {
  // --------------------------------------------------------------
  // ** coarsening 
  bool build_graph(std::vector< Element > *elts, 
                   Element parent,
                   //
                   std::vector< int > *xadj, 
                   std::vector< int > *adjncy,
                   std::vector< int > *adjwgt) {

#ifdef UHM_BUILD_GRAPH_MEASURE_TIME 
    printf(":: begin :: build_graph\n");
    double t, time;
#endif

    int n = elts->size();

    // ** mark the elements with parent id, and indexing
#ifdef UHM_BUILD_GRAPH_MEASURE_TIME
    t = timer();
#endif

    for (int i=0;i<n;++i) {
      elts->at(i)->set_marker(0, parent->get_id());
      elts->at(i)->set_marker(1, i);
    }

#ifdef UHM_BUILD_GRAPH_MEASURE_TIME
    time = timer()-t;
    printf(":: marking elts %lf\n", time);
#endif

    // ** create graph
    std::vector< std::map< int, int > > adj;
    adj.assign( n, std::map<int,int>() );
      
    // **loop through elts
#ifdef UHM_BUILD_GRAPH_MEASURE_TIME
    t = timer();
#endif
    
#pragma omp parallel for schedule(static)
    for (int i=0;i<n;++i) {
      Element elt = elts->at(i);
	  
      // loop through schur nodes
      std::map< Node, int >::iterator nit;
      for (nit=elt->nodes.begin();nit!=elt->nodes.end();++nit) {
	    
        if (nit->first->get_kind() == UHM_NODE_KIND_DEFAULT &&
            nit->second            == UHM_SEPARATED_SCHUR) {
          
          std::set< Element >::iterator eit;
          for (eit =(nit->first)->owner.begin();
               eit!=(nit->first)->owner.end();
               ++eit) {
		  
            // if owner has marker of parent id, add it in adjacency
            if ((*eit)->get_marker(0) == parent->get_id() &&
                (*eit) != elt) { 
              
              // vertex point and its weight
              std::pair< std::map< int, int >::iterator, bool > ret;
              std::pair< int,int > in;
              
              in.first  = (*eit)->get_marker(1);
              in.second = nit->first->get_n_dof();
              
              ret = adj[i].insert( in );
              
              if (ret.second==false) 
                ret.first->second += in.second;
            }
          }
        }
      }
    }

#ifdef UHM_BUILD_GRAPH_MEASURE_TIME
    time = timer()-t;
    printf(":: add schur nodes %lf\n", time);
#endif

    // ** remove duplicated entries
#ifdef UHM_BUILD_GRAPH_MEASURE_TIME
    t = timer();
#endif
    
    // clear input array
    if (xadj->size()) xadj->clear();
    if (adjncy->size()) adjncy->clear();
    if (adjwgt->size()) adjwgt->clear();
    
    xadj->reserve   ( n+1 );
    xadj->push_back ( 0 );
    
    for (int i=0;i<n;++i) {
      
      std::map<int,int>::iterator ait;
      for (ait=adj[i].begin();ait!=adj[i].end();++ait) {
        adjncy->push_back(ait->first);
        adjwgt->push_back(ait->second);
      }
      xadj->push_back(adjncy->size());
    }

#ifdef UHM_BUILD_GRAPH_MEASURE_TIME
    time = timer()-t;
    printf(":: map to vector %lf\n", time);
#endif

#ifdef UHM_BUILD_GRAPH_MEASURE_TIME 
    printf(":: end :: build_graph\n");
#endif
    
    for (int i=0;i<xadj->size();++i)
      std::cout << *xadj[i] << " ";
    std::cout << std::endl;
    
    for (int i=0;i<adjncy->size();++i)
      std::cout << *adjncy[i] << " ";
    std::cout << std::endl;
    
    for (int i=0;i<adjwgt->size();++i)
      std::cout << *adjwgt[i] << " ";
    std::cout << std::endl;

    return true;
  }
}
